package com.applcn.core.redis;

import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * com.applcn.core.redis
 * Description:
 *
 * @author: 大妖怪
 * @date: 2020-01-18 00:09
 */
@Component
@RequiredArgsConstructor
public class RedisDisLock {

    private final StringRedisTemplate stringRedisTemplate;
    private static String DEFAULT_LOCK_VALUE = "1";

    /**
     * 防重入锁，暂不支持可重入
     * 从spring-data-redis的2.1版本后可以使用单条语句执行SET key value [expiration EX seconds] [NX]指令
     *
     * @param key redis键
     * @return true: 无此key / false: 已经有此key
     * @see <a href="http://redis.cn/topics/distlock.html">Redis分布式锁规范</a>
     */
    public boolean lock(String key) {
        return lock(key, DEFAULT_LOCK_VALUE, 3, TimeUnit.SECONDS);
    }

    /**
     * 防重入锁
     *
     * @param key      redis key
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return true: 无此key / false: 已经有此key
     */
    public boolean lock(String key, long timeout, TimeUnit timeUnit) {
        return lock(key, DEFAULT_LOCK_VALUE, timeout, timeUnit);
    }

    /**
     * 防重入锁
     *
     * @param key      redis键
     * @param value    redis值
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return true: 无此key / false: 已经有此key
     */
    public boolean lock(String key, String value, long timeout, TimeUnit timeUnit) {
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
        if (success == null) {
            return false;
        }
        return success;
    }

    /**
     * 通过key获取value
     *
     * @param key
     * @return
     */
    public String getCode(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 删除防重入锁
     *
     * @param key redis键
     * @return true: 删除成功 / false: 删除失败
     */
    public boolean unlock(String key) {
        return unlock(key, DEFAULT_LOCK_VALUE);
    }

    /**
     * 删除防重入锁
     * lua脚本实现
     * if redis.call("get",KEYS[1]) == ARGV[1]
     * then
     * return redis.call("del",KEYS[1])
     * else
     * return 0
     * end
     *
     * @param key   redis键
     * @param value redis值
     * @return true: 删除成功 / false: 删除失败
     */
    public boolean unlock(String key, String value) {
        String redisValue = stringRedisTemplate.opsForValue().get(key);
        if (!StrUtil.equals(redisValue, value)) {
            return false;
        }
        Boolean success = stringRedisTemplate.delete(key);
        if (success == null) {
            return false;
        }
        return success;
    }
}
